-
Notifications
You must be signed in to change notification settings - Fork 33
fix Windows-specific path normalization using GetLongPathNameW #277
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR fixes issue #186 by replacing fs::canonicalize with Windows API GetLongPathNameW for path case normalization on Windows. The key improvement is that GetLongPathNameW does not resolve junction points, which was causing incorrect path resolution for virtual environments that use junctions.
Changes:
- Replaced
fs::canonicalizewith direct Windows API calls toGetLongPathNameWfor case normalization - Added comprehensive test coverage for junction preservation and various path formats
- Added windows-sys 0.59 dependency for Windows API access
Reviewed changes
Copilot reviewed 2 out of 3 changed files in this pull request and generated 7 comments.
| File | Description |
|---|---|
| crates/pet-fs/src/path.rs | Refactored norm_case function to use GetLongPathNameW instead of fs::canonicalize, added new normalize_case_windows helper function with comprehensive tests for junction preservation, case normalization, and path handling |
| crates/pet-fs/Cargo.toml | Added windows-sys 0.59 dependency with Win32_Storage_FileSystem and Win32_Foundation features for Windows API access |
| Cargo.lock | Updated to include windows-sys 0.59.0 dependency for pet-fs crate |
Comments suppressed due to low confidence (4)
crates/pet-fs/src/path.rs:82
- The comment states that GetLongPathNameW "may add \?\ prefix in some cases" but according to Microsoft documentation, GetLongPathNameW does not add the \?\ prefix - that's added by GetFullPathNameW or by the application itself. GetLongPathNameW typically removes the \?\ prefix if present. This comment may be misleading. Consider clarifying that this check handles cases where the path was already normalized or converted elsewhere, or if this is based on observed behavior, add a note about the specific cases where this occurs.
// Remove UNC prefix if original path didn't have it
// GetLongPathNameW may add \\?\ prefix in some cases
crates/pet-fs/src/path.rs:10
- The comment says GetLongPathNameW "preserves junction paths" but this might be slightly misleading. GetLongPathNameW doesn't specifically preserve junctions - it simply doesn't resolve them, whereas canonicalize does. The distinction is subtle but important. Consider rephrasing to "GetLongPathNameW normalizes case but does not resolve junctions/symlinks (unlike fs::canonicalize)" to be more precise about the behavior.
// Uses GetLongPathNameW which normalizes case but preserves junction paths.
crates/pet-fs/src/path.rs:342
- There is no test coverage for network UNC paths (e.g., "\server\share\path"). These paths have different characteristics than local drive paths and may behave differently with GetLongPathNameW. The trailing slash removal logic (lines 90-92) may also be incorrect for these paths. Consider adding test coverage for network paths to ensure they are handled correctly.
#[test]
#[cfg(windows)]
fn test_norm_case_windows_normalizes_slashes() {
// norm_case should convert forward slashes to backslashes (like canonicalize did)
let path_with_forward_slashes = PathBuf::from("C:/Windows/System32");
let result = norm_case(&path_with_forward_slashes);
assert!(
!result.to_string_lossy().contains('/'),
"Should convert forward slashes to backslashes, got: {:?}",
result
);
assert!(
result.to_string_lossy().contains('\\'),
"Should have backslashes, got: {:?}",
result
);
}
}
crates/pet-fs/src/path.rs:79
- Using to_string_lossy().to_string() here may lose information from the OsString if it contains invalid UTF-8 sequences. This is the second conversion that could lose data (first was on line 46). While Windows paths are typically valid Unicode, this double conversion increases the risk of data loss. Consider working with the OsString directly or using methods that preserve the original bytes.
let mut result_str = os_string.to_string_lossy().to_string();
crates/pet-fs/src/path.rs
Outdated
| let path_str = path.to_string_lossy().replace('/', "\\"); | ||
| let normalized_path = PathBuf::from(&path_str); |
Copilot
AI
Jan 15, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Converting the path to a string and back through replace operations may lose information for paths with invalid UTF-8 sequences. While Windows paths are typically valid Unicode, using to_string_lossy() can result in data loss if the path contains invalid sequences (replaced with U+FFFD). Consider using OsStr methods directly or documenting this limitation if it's acceptable for this use case.
This issue also appears in the following locations of the same file:
- line 79
…prove fallback behavior for non-existent paths
Fixes #186